package net.yunzhanyi.client.service.impl;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import net.yunzhanyi.client.constant.AccountConstant;
import net.yunzhanyi.client.constant.LoginConstant;
import net.yunzhanyi.client.domain.dto.ClientLoginUser;
import net.yunzhanyi.client.service.TokenService;
import net.yunzhanyi.common.core.constants.TokenConstants;
import net.yunzhanyi.common.core.utils.ObjectUtils;
import net.yunzhanyi.common.core.utils.UUIDUtils;
import net.yunzhanyi.common.redis.service.RedisService;
import net.yunzhanyi.common.web.util.JwtUtil;
import net.yunzhanyi.common.web.util.StringUtils;
import org.apache.commons.beanutils.BeanMap;
import org.apache.commons.beanutils.BeanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static net.yunzhanyi.common.core.constants.TokenConstants.SECRET;

/**
 * @author bestct
 * @date 2022/10/1
 * @type 类
 */
@Service
public class TokenServiceImpl implements TokenService {

    Logger logger = LoggerFactory.getLogger(TokenServiceImpl.class);

    private String issuer = "www.yunzhanyi.net";

    private final long timeout = 10;
    @Autowired
    private RedisService redisService;


    /**
     * 生成token
     *
     * @param loginUser
     * @return
     */
    @Override
    public String createToken(ClientLoginUser loginUser) {
        String uuid = UUIDUtils.fastSimpleUUID();
        Map<String, Object> claims = new HashMap<>();
        claims.put(LoginConstant.ACCOUNT_KEY, loginUser.getAid());
        claims.put(LoginConstant.UUID_KEY, uuid);
        //生成token令牌
        String token = createToken(claims);
        saveToken(uuid, loginUser);
        return token;
    }

    private void saveToken(String uuid, ClientLoginUser loginUser) {
        String redisTokenKey = getRedisTokenKey(uuid, loginUser.getAid().toString());
        Map<?, ?> map = objectToMap(loginUser);
        redisService.setCacheMap(redisTokenKey, map);
        redisService.expire(redisTokenKey, timeout, TimeUnit.DAYS);
    }

    /**
     * 根据附加信息生成token
     *
     * @param claims
     * @return
     */
    private String createToken(Map<String, Object> claims) {

        return Jwts.builder()
                .setClaims(claims)
                // 签发者
                .setIssuer(issuer)
                //加密算法和秘钥
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
    }

    /**
     * 验证token
     *
     * @param request
     * @param response
     */
    @Override
    public boolean verifyToken(String token,HttpServletRequest request, HttpServletResponse response) {
        if (StringUtils.isEmpty(token)) {
            return false;
        } else {
            Claims claims = parseToken(token);
            if (ObjectUtils.isNull(claims)) {
                return false;
            }
            String redisTokenKey = getRedisTokenKey(claims);
            Object uid = redisService.getCacheMapValue(redisTokenKey, AccountConstant.USER);
            if (ObjectUtils.nonNull(uid)) {
                //将用户aid和RedisTokenKey存入request
                long expire = redisService.redisTemplate.opsForHash().getOperations().getExpire(redisTokenKey, TimeUnit.DAYS);

                int expireDivisor = 5;

                if (expire < (timeout / expireDivisor)) {
                    //刷新token
                    redisService.expire(redisTokenKey, timeout, TimeUnit.DAYS);
                }
                String aid = claims.get(LoginConstant.ACCOUNT_KEY).toString();
                request.setAttribute(AccountConstant.RTK, redisTokenKey);
                request.setAttribute(AccountConstant.USER, uid.toString());
                request.setAttribute(AccountConstant.AUTH, aid);
                return true;
            }
        }

        return false;
    }

    @Override
    public void deleteToken(String token) {
        Claims claims = parseToken(token);
        String redisKey = getRedisTokenKey(claims);
        redisService.deleteObject(redisKey);
    }

    @Override
    public void accountPublic(HttpServletRequest request) {
        String token = getToken(request);
        logger.warn(token);
        if (!StringUtils.isEmpty(token)) {
            Claims claims = parseToken(token);
            if (!ObjectUtils.isNull(claims)) {
                String redisTokenKey = getRedisTokenKey(claims);
                Object uid = redisService.getCacheMapValue(redisTokenKey, AccountConstant.USER);
                if (ObjectUtils.nonNull(uid)) {
                    request.setAttribute(AccountConstant.USER, uid.toString());
                    return;
                }
            }
            request.setAttribute(AccountConstant.USER, "-1");
        }
    }

    @Override
    public String getToken(HttpServletRequest request) {
        // 获取请求携带的令牌
        return request.getHeader(TokenConstants.AUTHENTICATION);
    }

    @Override
    public String getWebToken(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();
        String token = null;
        if (cookies != null && cookies.length != 0) {
            for (Cookie cookie : cookies) {
                if ("token".equals(cookie.getName())) {
                    token = cookie.getValue();
                    break;
                }
            }
        }
        return token;
    }

    private Claims parseToken(String rawToken) {

        if (StringUtils.isNotEmpty(rawToken)) {

            Claims claims;
            try {
                claims = JwtUtil.parseToken(rawToken);
                return claims;
            } catch (Exception e) {
                logger.error("token解析失败");
            }
        }
        return null;
    }

    @Override
    public ClientLoginUser parseWebToken(String token) {

        Claims claims = parseToken(token);
        ClientLoginUser loginUser = null;

        if (!ObjectUtils.isNull(claims)) {
            String redisTokenKey = getRedisTokenKey(claims);
            Map<String, Object> cacheMap = redisService.getCacheMap(redisTokenKey);
            try {
                loginUser = mapToObject(cacheMap, ClientLoginUser.class);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return loginUser;
    }

    private String getRedisTokenKey(String uuid, String aid) {
        return LoginConstant.TOKEN_KEY + uuid + "_" + aid;
    }

    private String getRedisTokenKey(Claims claims) {
        if (ObjectUtils.isNull(claims)) {
            return "";
        }
        String uuid = (String) claims.get(LoginConstant.UUID_KEY);
        String aid = claims.get(LoginConstant.ACCOUNT_KEY).toString();
        return getRedisTokenKey(uuid, aid);
    }
    /**
     * java对象转map
     *
     * @param obj
     */
    public  Map<?,?> objectToMap(Object obj) {
        if (obj == null) {
            return null;
        }
        return new BeanMap(obj);
    }
    //map转java对象
    public <T> T mapToObject(Map<String, Object> map, Class<T> beanClass)
            throws Exception {
        if (map == null) {
            return null;
        }
        T obj = beanClass.newInstance();
        BeanUtils.populate(obj, map);
        return obj;
    }

}
